import { Scene } from '@babylonjs/core/scene';
import { AbstractControl, IAbstractControlOptions } from './abstract-control';
import { Control } from '@babylonjs/gui/2D/controls/control';
import { CreateHandleTo, IHandle, RemoveHandle } from '../../data/data-handle';
import { StackPanel } from '@babylonjs/gui/2D/controls/stackPanel';
import { TextBlock } from '@babylonjs/gui/2D/controls/textBlock';
import { InputText } from '@babylonjs/gui/2D/controls/inputText';
import { Utils } from '../../tool/utils';
import { UIKeyboard } from './ui-keyboard';


/**
 * ui输入控件参数对象
 * @param title 标题
 * @param isVertical 是否纵向布局
 * @param debug 绘制调试（仅开发中使用）
 * @param fontSize 字体大小
 * @param titleColor 标题颜色
 * @param fontFamily 字体
 * @param titleWidth 标题宽度
 * @param inputWidth 输入框宽度
 * @param inputHeight 输入框高度
 * @param inputColor 输入框颜色
 * @param inputBackground 输入框背景颜色
 * @param defaultValue 默认内容
 * @param spacing 标题与输入框的间隔留白
 * @param inputFontSize 输入内容字体大小
 * @param isNumber 是否数字输入
 * @param disableMobilePrompt 禁用移动端输入
 */
interface IUIInputOptions extends IAbstractControlOptions {
    title?: string,
    isVertical?: boolean,
    debug?: boolean,
    fontSize?: number,
    titleColor?: string,
    fontFamily?: string,
    titleWidth?: number,
    inputWidth?: number,
    inputHeight?: number,
    inputColor?: string,
    inputBackground?: string,
    defaultValue?: string | number,
    spacing?: number,
    inputFontSize?: number,
    isNumber?: boolean,
    disableMobilePrompt?: boolean,
}


/**
 * ui输入控件
 * @param name 名称
 * @param scene 所在场景
 * @param options 参数对象
 */
class UIInput extends AbstractControl {

    private _changeHandles: Array<IHandle<string | number>> = [];
    private _defaultValue!: string | number;
    private _headerColor!: string;
    private _inputColor!: string;
    private _inputBackground!: string;
    private _isNumber!: boolean;
    private _value!: string | number;

    /**
     * 页眉（标题）原始控件
     */
    public header!: TextBlock;

    /**
     * 原始输入控件
     */
    public input!: InputText;

    constructor(name: string, scene: Scene, options?: IUIInputOptions) {
        super(name, scene, options);
        this.header.onPointerDownObservable.add(this.breakCamera);
        this.input.onPointerDownObservable.add(this.breakCamera);
    }

    /**
     * 设置输入框内容
     */
    public set value(value: string | number) {
        this._value = this.inputString(value);
        this._value = this._isNumber ? Number.parseFloat(this._value) : this._value;
        this.input.text = this._value + '';
    }

    /**
     * 获取输入框内容
     */
    public get value(): string | number {
        return this._value;
    }

    public set isEnabled(enabled: boolean) {
        this.input.isEnabled = enabled;
        if (this.enabledStyle) this.enabledStyle(enabled, this);
    }

    public get isEnabled(): boolean {
        return this.input.isEnabled;
    }

    /**
     * 设置使用的软键盘
     * @param keyboard 软键盘控件
     */
    public useKeyboard(keyboard: UIKeyboard): void {
        this.input.onPointerDownObservable.add(() => {
            if (keyboard.connected) {
                if (keyboard.connected.id === this.id) {
                    return;
                }
            }
            keyboard.connect(null);
            keyboard.connect(this);
            keyboard.display = true;
        });
    }

    /**
     * 添加输入改变时的回调函数对象
     * @param func 回调函数
     * @returns 回调函数对象
     */
    public onChange(func: { (value: string | number): void }): IHandle<string | number> {
        return CreateHandleTo(func, this._changeHandles);
    }

    /**
     * 移除输入改变时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeChangeHandle(handle: number | IHandle<string | number>): void {
        RemoveHandle(handle, this._changeHandles);
    }

    protected initializeControl(name: string, scene: Scene, options: IUIInputOptions): Control {
        const panel = new StackPanel(name + '-panel');
        panel.isVertical = options.isVertical || false;
        if (options.debug) panel.background = '#ff00ff';

        const fontSize = options.fontSize || 26;
        const fontFamily = options.fontFamily || 'Arial';
        const text = options.title || 'input';
        this._defaultValue = (options.defaultValue + '') || '';
        this._value = this._defaultValue;
        this._headerColor = options.titleColor || '#000000';
        this._inputColor = options.inputColor || '#ffffff';
        this._inputBackground = options.inputBackground || '#000000';
        this._isNumber = options.isNumber || false;

        const header = new TextBlock(name + '-header');
        header.text = text;
        header.fontFamily = fontFamily;
        header.color = this._headerColor;
        header.fontSize = fontSize + 'px';
        header.width = (options.titleWidth || (Utils.byteLength(text) * 0.5 * fontSize)) + 'px';
        header.height = fontSize + 'px';
        this.header = header;
        panel.addControl(header);

        const input = new InputText(name + '-input');
        input.text = this._defaultValue;
        input.fontSize = (options.inputFontSize || fontSize) + 'px';
        input.fontFamily = fontFamily;
        input.color = this._inputColor;
        input.background = this._inputBackground;
        input.width = (options.inputWidth || fontSize * 3) + 'px';
        input.height = (options.inputHeight || fontSize * 1.4) + 'px';
        input.thickness = 1e-6;
        input.disableMobilePrompt = options.disableMobilePrompt === undefined ? true : options.disableMobilePrompt;
        this.input = input;
        panel.addControl(input);

        const space = options.spacing || 5;
        UIInput.autoLayout(panel, header, space, [input], [input]);

        return panel;
    }

    protected bindEvent(control: Control, options: IUIInputOptions): void {
        this.input.onTextChangedObservable.add((input: InputText) => {
            if (!this.isEnabled) return;
            if (!this._isNumber) {
                if (this._value === input.text) return;
                this._value = input.text;
                if (!this.avoidEventHandle) this._changeHandles.forEach(handle => handle.callback(this._value));
                return;
            }
            const content = this.inputString(input.text);
            this.input.text = content;
            const v = Number.parseFloat(content) || 0;
            if (this._value === v) return;
            this._value = v;
            if (!this.avoidEventHandle) this._changeHandles.forEach(handle => handle.callback(this._value));
        });
        this.input.onBlurObservable.add((_input: Control) => {
            const input = _input as InputText;
            if (!this.isEnabled) return;
            if (input.text === '') {
                input.text = this.inputString(this._defaultValue);
            }

            if (!this._isNumber) {
                if (this._value === input.text) return;
                this._value = input.text;
                if (!this.avoidEventHandle) this._changeHandles.forEach(handle => handle.callback(this._value));
                return;
            }
            const content = this.inputString(input.text);
            const v = Number.parseFloat(content) || 0;
            this.input.text = v + '';
            if (this._value === v) return;
            this._value = v;
            if (!this.avoidEventHandle) this._changeHandles.forEach(handle => handle.callback(this._value));
        });
    }

    protected unbindEvent(control: Control): void {
        super.unbindEvent(control);
        this.input.onTextChangedObservable.clear();
        this.input.onBlurObservable.clear();
    }

    /**
     * 根据当前输入类型处理输入的字符串
     * @param text 输入的字符串
     * @returns 处理后的字符串
     */
    private inputString(text: string | number): string {
        if (text === '') return '';
        const tex = text + '';
        if (this._isNumber) {
            return tex.replace(/[^0-9|.|-]/g, '');
        } else {
            return text + '';
        }

    }

}


/**
 * 创建ui输入控件
 * @param name 名称
 * @param scene 所在场景
 * @param options 参数对象
 * @param changeCallback 输入改变时的回调函数
 * @returns ui输入控件
 */
const CreateUIInput = (name: string, scene: Scene, options?: IUIInputOptions, changeCallback?: { (value: string | number): void }): UIInput => {
    const input = new UIInput(name, scene, options);
    if (changeCallback) input.onChange(changeCallback);
    return input;
};


export { UIInput, CreateUIInput };
export type { IUIInputOptions };